package org.pb.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.omg.CORBA.TIMEOUT;
import org.pb.basic.constant.WxConstants;
import org.pb.basic.service.impl.BaseServiceImpl;
import org.pb.basic.utils.AjaxResult;
import org.pb.basic.utils.HttpClientUtils;
import org.pb.basic.utils.MD5Utils;
import org.pb.basic.utils.StrUtils;
import org.pb.user.domain.LoginInfo;
import org.pb.user.domain.WxUser;
import org.pb.user.dto.LoginDto;
import org.pb.user.mapper.LoginInfoMapper;
import org.pb.user.mapper.WxUserMapper;
import org.pb.user.query.LoginInfoQuery;
import org.pb.user.service.ILoginInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 登录信息表业务层接口的实现类
 * @author pb
 */
@Service
public class LoginInfoServiceImpl extends BaseServiceImpl<LoginInfo, LoginInfoQuery> implements ILoginInfoService {

    @Autowired
    private LoginInfoMapper loginInfoMapper;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Override
    @Transactional
    public void save(LoginInfo condition) {
        super.save(condition);
    }

    @Override
    @Transactional
    public void update(LoginInfo condition) {
        super.update(condition);
    }

    @Override
    @Transactional
    public void remove(Long id) {
        super.remove(id);
    }

    @Override
    public AjaxResult loginRetype(LoginDto loginDto) {
        // 判断账号或者密码是否为空，如果有一个为空，则抛出异常
        if (loginDto.getUsername() == null || loginDto.getPassword() == null) {
            throw new RuntimeException("账号或密码不能为空，请正确填写");
        }
        // 通过用户名和类型查询数据
        LoginInfo loginInfo = loginInfoMapper.getByNameAndType(loginDto);
        // 如果loginInfo为空，则表示没有该用户名，抛出异常
        if (loginInfo == null) {
            throw new RuntimeException("账号或密码错误，请正确填写");
        }
        // 因为数据库中的密码经过了加密处理，所以需要将数据传输对象中的密码也经过加密处理，在和数据库中的密码进行比较
        String newPassword = loginDto.getPassword() + loginInfo.getSalt();
        newPassword = MD5Utils.encrypByMd5(newPassword);
        // 判断数据传输对象中的密码和数据库中的密码是否一致，如果不一致，表示密码错误
        if (!newPassword.equals(loginInfo.getPassword())) {
            throw new RuntimeException("账号或密码错误，请正确填写");
        }
        // 判断loginInfo对象中的disable参数的值是否为false
        if (!loginInfo.getDisable()) {
            // 如果登陆对象处于禁用状态，抛出异常，用户处于的冻结状态
            throw new RuntimeException("账户处于冻结状态，请联系客服咨询详情");
        }
        // 如果账号密码正确，则表示该用户登陆成功，添加对应的登陆状态Token，并保存到对应的redis中
        // 通过UUID生成随机token字符串
        String token = UUID.randomUUID().toString();
        // 将loginInfo对象中的密码和盐值清空,防止数据泄露
        loginInfo.setPassword(null);
        loginInfo.setSalt(null);
        // 以随机生成的token为key，loginInfo对象为value，生命周期为30分钟，保存到redis中
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);
        // 通过一个Map接收token和loginInfo数据，然后保存到AjaxResult中，
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("loginInfo", loginInfo);
        return AjaxResult.successWithData(map);
    }

    @Override
    public AjaxResult wechatLogin(Map<String, String> map) {
        // 根据code获取到对应的授权码
        String code = map.get("code");
        System.err.println(code);
        // 将常量中的请求替换为对应的参数
        String url = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replaceAll("CODE", code);
        // 调用httpClient工具类，发送授权码。获取到对应的微信用户的openid
        String result = HttpClientUtils.httpGet(url);
        // 通过fastJson解析得到的json字符串,使用JSONObject.parseObject方法，得到对应的JSONObject对象
        JSONObject res = JSONObject.parseObject(result);
        // 通过得到的结果对象，利用getString方法获取到结果json对象中的字符串内容
        String access_token = res.getString("access_token");
        String openid = res.getString("openid");
        // 获取到微信用户的资源访问权限token和对应的opendi后，就需要判断对应的微信用户是否存在，首先获取到微信用户数据
        WxUser wxUser = wxUserMapper.getByOpenId(openid);
        HashMap<String, Object> binders = new HashMap<>();
        // 如果微信用户不存在或者微信用户的user_id不存在，则提示需要绑定用户
        if (wxUser == null || wxUser.getUser_id() == null) {
            // 回传当前微信用户的openid和access_token，用于绑定微信用户信息和当前平台用户的信息
            binders.put("access_token", access_token);
            binders.put("openid", openid);
            return AjaxResult.failedWithData(binders);
        }
        // 如果当前微信用户存在，且已经绑定了平台账号，则直接免密登陆，首先获取到当前的登陆token
        String token = UUID.randomUUID().toString();
        System.err.println(token);
        // 然后根据平台用户id获取到对应的登陆状态
        Long user_id = wxUser.getUser_id();
        LoginInfo loginInfo = loginInfoMapper.getByUserId(user_id);

        // 将loginInfo中的密码和盐值清空
        loginInfo.setSalt(null);
        loginInfo.setPassword(null);
        // 将当前的token作为key，loginInfo作为value，保存到Redis中，生命周期为三十分钟
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);

        // 免密登陆，并返回当前的登陆状态
        binders.put("token", token);
        binders.put("loginInfo", loginInfo);
        return AjaxResult.successWithData(binders);
    }
}
